﻿#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
//一般内置类型相互转换时都会生成临时变量，最典型的情况为：int->double,float->double,char->int,short->long
//其余情况可能会由于编译器优化导致不生成（箭头可取反，但取反的情况也有不生成的可能）
//而涉及到自定义类的转换时，就需要自己编写构造函数才能实现了，如int 转换为dzh1，就需要：dzh1(int x)
//若没有对应的构造函数，则编译器会报错
//注：隐式生成的临时对象可能导致性能损耗或逻辑错误，且不易追踪，因此，不推崇依赖类的隐式类型转换

class dzh1
{
public:
	dzh1(int a = 0)
	{
		std::cout << "调用了默认构造函数" << std::endl;
		_a = a;
	}
	//显式写出拷贝构造，方便观察调用情况
	dzh1(const dzh1& s)
	{
		std::cout << "调用了拷贝构造函数" << std::endl;
		_a = s._a;
	}
	void Print()
	{
		std::cout << _a << std::endl;
	}
	void Push(dzh1 a)
	{
		std::cout << "调用了Push函数" << std::endl;
	}
private:
	int _a;
};

void test1()
{
	std::cout << "a: " << std::endl;
	dzh1 a(1);//调用了默认构造函数
	std::cout << "b: " << std::endl;
	dzh1 b = 2;//调用了默认构造函数->这里在语法上其实与a的初始化不同，
	//b这里实际上是先将整形字面量2通过默认构造函数隐式转换成了dzh1类型，然后通过拷贝构造函数将其拷贝给b

	/*实际过程：
	dzh1 ls(2);
	dzh1 b = ls;*/
	//但由于 dzh1 b = 42;的写法规定了要使用“拷贝省略”来避免函数调用的冗余，因此，真正运行时只会调用一次默认构造

	std::cout << "c: " << std::endl;
	dzh1 c = 2.2;//这里其实是先将2.2隐式转换为匹配默认构造参数的int类型，然后再将其转换为dzh1类型，再进行拷贝
	c.Print();//2

	//dzh1 d = "as";//由于字符串类型不能隐式转换为int类型，所以这里出现了类型不匹配的报错


	//类成员函数的隐式类型转换不止出现在默认构造函数中：
	dzh1 stack(1);
	std::cout << "stack: " << std::endl;
	stack.Push(1);//调用了Push函数->在不优化的情况下，该行代码实际上是先将1通过默认构造函数隐式转换为dzh1类型
	//然后再传给Push函数作为参数
/*->即：
dzh1 e(1);
stack.Push(e);*/
}


//原本，此语法只能在传单个参数时生效，但在后续C++版本（​​C++11 后​​），可以对多个参数进行隐式转换传参了，示例如：
class stack
{
public:
	stack(int a = 0, int b = 1)
	{
		std::cout << "调用了默认构造函数" << std::endl;
		_a = a;
		_b = b;
	}
	//显式写出拷贝构造，方便观察调用情况
	stack(const stack& s)
	{
		std::cout << "调用了拷贝构造函数" << std::endl;
		_a = s._a;
		_b = s._b;
	}
	void Print()
	{
		std::cout << _a << " " << _b << std::endl;
	}
	void Push(stack a)
	{
		std::cout << "调用了Push函数" << std::endl;
	}
private:
	int _a;
	int _b;
};

void test2()
{
	std::cout << "a: " << std::endl;
	stack a = { 1,1 };//与单参数的差别就是多参数需要加大括号进行修饰
	a.Print();//1 1\0

	std::cout << "b: " << std::endl;
	stack b = { 3, 5 }; //调用了默认构造函数
	b.Push({ 3,3 });//调用了默认构造函数\n调用了Push函数
	//->可以发现，虽然优化后仍未显式调用拷贝构造函数，但Push多参数时显式调用了默认构造函数，足以说明多参数优化不如单参数般严格
}

int main()
{
	//test1();
	test2();
	return 0;
}



//综上所述，隐式类型转换有优点（代码便捷，减少冗余），但也有缺点（降低了代码可读性）
//同时，如果传参类型错误，按照上述隐式转换，是不会报错的，为此，C++提供了禁止类成员函数中参数发生隐式类型转换的关键字：explicit

//explicit 关键字仅对自定义类型（用户定义的类或结构体）有效​​，它用于修饰类的 ​​构造函数​​ 或 ​​类型转换运算符​​，
//作用是禁止编译器在隐式类型转换时自动调用这些函数

//explicit关键字的使用方法是在构造函数前加入explicit（只能在构造函数前加，因为其他函数隐式类型转换时本来就都需要通过构造函数，虽然进行了优化，但总归还是需要的）
class dzh1
{
public:
	explicit dzh1(int a = 0)//在函数前加explicit即可避免隐式类型转换
	{
		std::cout << "调用了默认构造函数" << std::endl;
		_a = a;
	}
	void Push(dzh1 a)
	{
		std::cout << "调用了Push函数" << std::endl;
	}
private:
	int _a;
};

int main()
{
	//dzh1 a = 1;//可以发现，此种写法由于不能隐式转换，已经失效
	//a.Push(2);//该写法也被禁止
	return 0;
}

